package com.ilink.dataanalysis.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ilink.dataanalysis.entity.PO.UserActionLogInfo;
import com.ilink.dataanalysis.entity.VO.*;
import com.ilink.dataanalysis.entity.VO.mapdata.MapData;
import com.ilink.dataanalysis.entity.VO.mapdata.MapSchoolData;
import com.ilink.dataanalysis.entity.VO.rankings.Rankings;
import com.ilink.dataanalysis.entity.VO.rankings.RankingsColumn;
import com.ilink.dataanalysis.entity.VO.rankings.RankingsRow;
import com.ilink.dataanalysis.entity.VO.statistics.EnrollmentSeries;
import com.ilink.dataanalysis.entity.VO.statistics.EnrollmentStatistics;
import com.ilink.dataanalysis.entity.VO.statistics.UserSeries;
import com.ilink.dataanalysis.entity.VO.statistics.UserStatistics;
import com.ilink.dataanalysis.mapper.TbUserMapper;
import com.ilink.dataanalysis.service.TbUserService;
import com.ilink.ilinkcommon.domain.User;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class TbUserServiceImpl extends ServiceImpl<TbUserMapper, User> implements TbUserService {

    private final TbUserMapper tbUserMapper;

    private final ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public int getDifferentSchoolCount() {
        try {
            return tbUserMapper.getDifferentSchoolCount();
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return -1;
        }
    }

    @Override
    public int getDifferentMajorCount() {
        try {
            return tbUserMapper.getDifferentMajorCount();
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return -1;
        }
    }

    @Override
    public int getStudentCount() {
        try {
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            return tbUserMapper.selectCount(queryWrapper);
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return -1;
        }
    }

    @Override
    public Rankings getResourceAccessTime() {
        try {
            List<RankingsRow> rows = new ArrayList<>();
            rows.add(new RankingsRow("数据广场", this.getAccessTimesWithApiRoute("/ground/dataset/get_common_dataset_tags")));
            rows.add(new RankingsRow("应用广场", this.getAccessTimesWithApiRoute("/ground/langflow_app/tags")));
            rows.add(new RankingsRow("双创广场项目展示", this.getAccessTimesWithApiRoute("/ground/innovationPlaza/project/getProjectDetailsMeta")));
            rows.add(new RankingsRow("学习广场技能树资源", this.getAccessTimesWithApiRoute("/teacher/study_tree_json/getCommonStudyTreeTags")));
            rows.add(new RankingsRow("学习广场课程资源", this.getAccessTimesWithApiRoute("/ground/study/course/add_course_collection")));
            List<RankingsColumn> columns = List.of(new RankingsColumn("资源", "resource"), new RankingsColumn("访问数", "visitors"));
            return new Rankings(columns, rows);
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }

    @Override
    public EnrollmentStatistics getEnrollmentStatistics() {
        try {
            List<String> grades = List.of("大一", "大二", "大三", "大四", "硕士研究生", "博士研究生");
            List<BigDecimal> count = new ArrayList<>();
            Map<String, BigDecimal> map = tbUserMapper.getStudentGradeAndCount();
            for(String grade : grades){
                count.add(map.get(grade));
            }
            EnrollmentSeries series = new EnrollmentSeries("name", count);
            List<EnrollmentSeries> seriesList = List.of(series);
            return new EnrollmentStatistics(grades, seriesList);
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }


    @Override
    public List<MajorInfo> getMajorAndCount() {
        try {
            return tbUserMapper.getMajorAndCount();
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }

    @Override
    public MapData getSchoolAndItsStudent() {
        try {
            List<MapSchoolData> mapSchoolData = new ArrayList<>();
            for (Map<String, Object> entry : tbUserMapper.getSchoolAndItsStudent()) {
                String schoolName = (String) entry.get("school_name");
                Long studentCount = (Long) entry.get("student_count");
                mapSchoolData.add(new MapSchoolData(schoolName, List.of(studentCount), studentCount/2));
            }
            return new MapData(mapSchoolData, List.of("报名人数"), List.of("人"));
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }

    @Override
    public List<Integer> getDailyTotalUser() {
        try {
            List<Integer> dailyUser = tbUserMapper.getNewUserOfEveryDay();
            int previousUserCount = tbUserMapper.getPreviousUserCount();
            dailyUser.set(0, dailyUser.get(0) + previousUserCount);
            for (int i = 1; i < dailyUser.size(); i++) {
                dailyUser.set(i, dailyUser.get(i) + dailyUser.get(i - 1));
            }
            return dailyUser;
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }

    @Override
    public List<Integer> getPastTenDaysLoginAccessTimes() {
        try {
            List<Integer> pastTenDaysLoginAccessTimes = new ArrayList<>();
            LocalDate currentDate = LocalDate.now();
            for (int i = 10; i >= 1; i--) {
                pastTenDaysLoginAccessTimes.add(getCertainDayLoginAccessTimes(currentDate.minusDays(i)));
            }
            return pastTenDaysLoginAccessTimes;
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }

    @Override
    public UserStatistics getUserStatistics() {
        try {
            List<UserSeries> series = new ArrayList<>();
            series.add(new UserSeries("当日活跃用户人次", this.getPastTenDaysLoginAccessTimes(), "bar", 0));
            series.add(new UserSeries("累计用户人数", this.getDailyTotalUser(), "line", 1));
            List<String> categories = new ArrayList<>();
            LocalDate today = LocalDate.now();
            for (int i = 10; i >= 1; i--) {
                categories.add(today.minusDays(i).toString());
            }
            return new UserStatistics(series, categories);
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return null;
        }
    }

    @Override
    public int getAccessTimesWithApiRoute(String apiRoute) {
        try {
            // 获取10天前的日期
            LocalDate startDate = LocalDate.now().minusDays(10);
            // 构建布尔查询，结合两个条件
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                    .must(QueryBuilders.termQuery("api_route", apiRoute))  //指定接口
                    .must(QueryBuilders.rangeQuery("event_time")  //指定时间范围
                            .gte(startDate.atStartOfDay())
                            .lt(LocalDate.now().atStartOfDay()));
            // 创建查询对象
            Query query = new NativeSearchQuery(boolQueryBuilder);
            // 执行查询并获取符合条件的文档总数
            return (int) elasticsearchRestTemplate.count(query, UserActionLogInfo.class);
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return -1;
        }
    }

    @Override
    public int getCertainDayLoginAccessTimes(LocalDate date) {
        try {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                    .must(QueryBuilders.termQuery("api_route", "/user/school_login"))
                    .must(QueryBuilders.rangeQuery("event_time")
                            .gte(date.atStartOfDay())
                            .lte(date.plusDays(1).atStartOfDay()));
            Query query = new NativeSearchQuery(boolQueryBuilder);
            return (int) elasticsearchRestTemplate.count(query, UserActionLogInfo.class);
        } catch(Exception e) {
            log.info("内部服务器错误：" + e.getMessage());
            return -1;
        }
    }

}